package org.takeback.chat.lottery;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.lang3.StringUtils;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.takeback.chat.lottery.listeners.GameException;
import org.takeback.chat.lottery.listeners.RoomAndLotteryListener;
import org.takeback.chat.service.GameMonitor;
import org.takeback.util.JSONUtils;
import org.takeback.util.exception.CodedBaseRuntimeException;
import org.takeback.util.identity.SerialNumberGenerator;

import com.google.common.collect.Maps;

public class DefaultLottery implements Lottery {
	private static final Logger log = LoggerFactory.getLogger(DefaultLottery.class);
	private static final String ID_PREFIX = "LT";
	private Lock lock = new ReentrantLock();
	private String id;
	private BigDecimal money;
	private Integer number;
	private AtomicInteger restNumber;
	private BigDecimal restMoney;
	private Integer sender;
	private Random random;
	private String roomId;
	private String description;
	private String title;
	private Date createTime;
	private String status = "0";
	private String type = "1";
	private Integer expiredSeconds = Integer.valueOf(60);
	private AtomicBoolean open = new AtomicBoolean(true);
	private GameMonitor monitor;
	private Map<Integer, LotteryDetail> detail;
	private Map<String, List<Double>> redRandom;//红包
	private Map<String, List<Integer>> redRandomLast;//红包尾数
	private RoomAndLotteryListener roomAndLotteryListener;
	private Integer marsterId;//房主id
	private Boolean marsterHadGet;//房主是否已经抢了
	private String roomType;
	
	public String getRoomType() {
		return roomType;
	}

	public void setRoomType(String roomType) {
		this.roomType = roomType;
	}

	public Boolean getMarsterHadGet() {
		return marsterHadGet;
	}

	public void setMarsterHadGet(Boolean marsterHadGet) {
		this.marsterHadGet = marsterHadGet;
	}

	public Integer getMarsterId() {
		return marsterId;
	}

	public void setMarsterId(Integer marsterId) {
		this.marsterId = marsterId;
	}

	public List<Integer> getRedRandomLast(String key) {
		return redRandomLast.get(key);
	}

	public void setRedRandomLast(Map<String, List<Integer>> redRandomLast) {
		this.redRandomLast = redRandomLast;
	}

	public void setRedRandom(Map<String, List<Double>> redRandom) {
		this.redRandom = redRandom;
	}

	public DefaultLottery(BigDecimal money, Integer number) {
        if (money.doubleValue() / number < 0.01) {
            throw new CodedBaseRuntimeException(500, "lottery create failed, each is must more than 0.01");
        }
        this.id = SerialNumberGenerator.generateSequenceNo("LT");
        this.restMoney = money;
        this.money = money;
        this.number = number;
        this.restNumber = new AtomicInteger(number);
        this.random = new Random();
        this.createTime = new Date();
		this.detail = Maps.newLinkedHashMap();
		this.monitor = GameMonitor.getInstance();
	}


	private BigDecimal allocate(int uid) {
		int temp = this.restNumber.getAndDecrement();
		if (temp < 1) {
			throw new CodedBaseRuntimeException(515,
					String.format("lottery %s is already finished", new Object[] { this.id }));
		}
		if (temp == 1) {
			BigDecimal last = this.restMoney;
			this.restMoney = BigDecimal.ZERO;
			return last;
		}
		BigDecimal expect = null;
		if (StringUtils.isNotEmpty(this.roomId)) {
			expect = this.monitor.getOne(this.roomId, Integer.valueOf(uid));
		}
		BigDecimal result = null;
        if (expect != null && expect.doubleValue() > 0.0 && expect.doubleValue() < this.restMoney.doubleValue() / 2.0) {
            result = expect;
            this.monitor.deleteOne(this.roomId, uid);
        }
        else {
            final Integer restCoin = this.restMoney.multiply(BigDecimal.valueOf(100L)).intValue();
            final Integer maxCoin = this.restMoney.multiply(BigDecimal.valueOf(200L)).divide(BigDecimal.valueOf(temp), 0, 3).intValue();
            Integer getCoin = this.random.nextInt(maxCoin) + 1;
            if (restCoin - getCoin < temp - 1) {
                getCoin = restCoin - (temp - 1);
            }
            result = new BigDecimal(getCoin).divide(BigDecimal.valueOf(100L));
        }
        this.restMoney = this.restMoney.subtract(result);
        return result;
	}

	public BigDecimal open(int uid) throws GameException {
		this.lock.lock();
		try {
			if (!isOpen()) {
				throw new GameException(510, String.format("lottery %s is already closed", new Object[] { this.id }));
			}
			if (0 == this.restNumber.get()) {
				throw new GameException(514, String.format("lottery %s is already finished", new Object[] { this.id }));
			}
			if (isExpired()) {
				expired();
				throw new GameException(511, String.format("lottery %s is already expired", new Object[] { this.id }));
            }
            if (this.detail.containsKey(uid)) {
                throw new GameException(512, String.format("user %s have opend this lottery %s", uid, this.id));
            }
            if (this.roomAndLotteryListener != null && !this.roomAndLotteryListener.onBeforeOpen(uid, this)) {
                throw new GameException(513, String.format("lottery %s is not allow to open", this.id));
            }
            if("G04".equals(this.getRoomType()) && !this.getMarsterHadGet() &&  this.restNumber.get() > 1 && uid != this.getMarsterId()) {
           	 BigDecimal result2 = this.allocate(uid);
           	 LotteryDetail lotteryDetail = new LotteryDetail(this.getMarsterId(), result2);
                this.detail.put(this.getMarsterId(), lotteryDetail);
                if (this.roomAndLotteryListener != null) {
                    this.roomAndLotteryListener.onOpen(this, lotteryDetail);
                }
                if (0 == this.restNumber.get()) {
                    this.finished();
                    return result2;
                }
                this.setMarsterHadGet(true);
           }
            BigDecimal result = this.allocate(uid);
            //如果是龙虎合
            if("LHH0001".equals(this.getRoomId())) {
            	int temp = this.restNumber.get();
            	System.out.println("当前取到的红包值是：" + temp);
            	List<Double> list = this.redRandom.get(this.getId());
            	try {
					result = BigDecimal.valueOf(list.get(temp)).setScale(2,BigDecimal.ROUND_DOWN);;
					System.out.println(uid + "领取了"+result+"的红包");
				} catch (Exception e) {
					System.out.println("领红包出错");
				}
            }
            
            LotteryDetail lotteryDetail = new LotteryDetail(uid, result);
            this.detail.put(uid, lotteryDetail);
            
//            userService.shareYongjin(uid, new Double(1), this.getr);
            if (this.roomAndLotteryListener != null) {
                this.roomAndLotteryListener.onOpen(this, lotteryDetail);
            }
            if (0 == this.restNumber.get()) {
                this.finished();
            }
			return result;
		} finally {
			this.lock.unlock();
		}
	}

	public BigDecimal fakeOpen(int uid) throws GameException {
		this.lock.lock();
		try {
            if (0 == this.restNumber.get()) {
                this.finished();
                throw new GameException(514, String.format("lottery %s is already finished", this.id));
            }
            if (this.detail.containsKey(uid)) {
                throw new GameException(512, String.format("user %s have opend this lottery %s", uid, this.id));
            }
            if (this.roomAndLotteryListener != null && !this.roomAndLotteryListener.onBeforeOpen(uid, this)) {
                throw new GameException(513, String.format("lottery %s is not allow to open", this.id));
            }
			BigDecimal result = allocate(uid);
            LotteryDetail lotteryDetail = new LotteryDetail(uid, result);
			this.detail.put(Integer.valueOf(uid), lotteryDetail);
			if (this.roomAndLotteryListener != null) {
				this.roomAndLotteryListener.onOpen(this, lotteryDetail);
			}
			if (0 == this.restNumber.get()) {
				finished();
			}
			return result;
		} finally {
			this.lock.unlock();
		}
	}

	public Integer getRestNumber() {
		return Integer.valueOf(this.restNumber.get());
	}

	public void finished() throws GameException {
		if (this.open.compareAndSet(true, false)) {
			if (this.roomAndLotteryListener != null) {
				this.roomAndLotteryListener.onFinished(this);
			}
			setStatus("1");
		}
	}

	public void expired() throws GameException {
		if (this.expiredSeconds.intValue() == 0) {
			return;
		}
		if (this.open.compareAndSet(true, false)) {
			if (this.roomAndLotteryListener != null) {
				this.roomAndLotteryListener.onExpired(this);
			}
			setStatus("2");
		}
	}

	public boolean isExpired() {
		if (this.expiredSeconds.intValue() == 0) {
			return false;
		}
		if (this.status.equals("2")) {
			return true;
		}
		LocalDateTime now = LocalDateTime.now();
		return now.minusSeconds(this.expiredSeconds.intValue()).toDate().after(this.createTime);
	}

	public Integer getExpiredSeconds() {
		return this.expiredSeconds;
	}

	public void setExpiredSeconds(Integer expiredSeconds) {
		this.expiredSeconds = expiredSeconds;
	}

	public void setRoomAndLotteryListener(RoomAndLotteryListener roomAndLotteryListener) {
		this.roomAndLotteryListener = roomAndLotteryListener;
	}

	public String getType() {
		return this.type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public BigDecimal getMoney() {
		return this.money;
	}

	public void setMoney(BigDecimal money) {
		this.money = money;
		this.restMoney = money;
	}

	public Integer getNumber() {
		return this.number;
	}

	public void setNumber(Integer number) {
		this.number = number;
	}

	public Integer getSender() {
		return this.sender;
	}

	public void setSender(Integer sender) {
		this.sender = sender;
	}

	public String getId() {
		return this.id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getRoomId() {
		return this.roomId;
	}

	public void setRoomId(String roomId) {
		this.roomId = roomId;
	}

	public String getDescription() {
		return StringUtils.isEmpty(this.description) ? "恭喜发财,大吉大利!" : this.description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public Date getCreateTime() {
		return this.createTime;
	}

	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}

	public String getStatus() {
		return this.status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public BigDecimal getRestMoney() {
		return this.restMoney;
	}

	public boolean isOpen() {
		return this.open.get();
	}

	public void addDetail(LotteryDetail lotteryDetail) {
		this.detail.put(lotteryDetail.getUid(), lotteryDetail);
	}

	public Map<Integer, LotteryDetail> getDetail() {
		return this.detail;
	}

	public String toString() {
		StringBuffer sb = new StringBuffer("id:").append(this.id).append("-").append(this.status).append("  ");
		Map<Integer, LotteryDetail> lts = getDetail();

		Iterator itr = lts.keySet().iterator();
		while (itr.hasNext()) {
			Integer key = Integer.valueOf(itr.next().toString());
			LotteryDetail detail = (LotteryDetail) lts.get(key);
			if (detail != null) {
				sb.append(detail.toString()).append("\r\n");
			}
		}
		return sb.toString();
	}

	public static void main(String[] args) {
		DefaultLottery lottery = new DefaultLottery(BigDecimal.valueOf(100L), Integer.valueOf(6));
		int i = 1;
		try {
			for (;;) {
				BigDecimal result = lottery.open(i);
				System.out.println(result);
				i++;
			}
		} catch (Exception e) {
			System.out.println(JSONUtils.toString(lottery.getDetail()));
		}
	}

	public String getTitle() {
		return this.title;
	}

	public void setTitle(String title) {
		this.title = title;
	}
}
